package cn.wygandwdn.core.transport.client;

import cn.wygandwdn.common.message.RpcRequest;
import cn.wygandwdn.common.utils.IdWorker;
import cn.wygandwdn.core.annotation.RpcReference;
import cn.wygandwdn.core.config.Config;
import cn.wygandwdn.core.failover.FailoverRetry;
import cn.wygandwdn.core.failover.impl.FailoverRetryImpl;
import io.netty.util.concurrent.Promise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @description:
 * @author: WYG
 * @time: 2021/10/27 18:44
 */

public class RpcClientProxy implements InvocationHandler {

    private static final Logger log = LoggerFactory.getLogger(RpcClientProxy.class);

    private final int timeout;
    private final RpcClient client;
    private final IdWorker idWorker;
    private final String version;
    private final String group;
    private final FailoverRetry retry;

    public RpcClientProxy(RpcClient client, String group, String version) {
        this.client = client;
        this.idWorker = Config.getIdWorker();
        this.group = group;
        this.version = version;
        this.timeout = Config.getTimeout();
        this.retry = new FailoverRetryImpl();
    }

    public <T> T getProxy(Class<T> cls) {
        return (T) Proxy.newProxyInstance(cls.getClassLoader(), new Class[] {cls}, this);
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 请求id
        long sequenceId = idWorker.nextId();
        RpcRequest request = new RpcRequest.Builder(method.getDeclaringClass().getName())
                .sequenceId(sequenceId)
                .method(method.getName())
                .paramType(method.getParameterTypes())
                .param(args)
                .group(group)
                .version(version)
                .build();
        Promise<Object> promise = client.sendRequest(request);
        // 等待promise的结果
        promise.await(timeout);

        if (promise.isSuccess()) {
            return promise.get();
        } else {
            if (promise.cause() != null) {
                throw new RuntimeException(promise.cause());
            } else {
                return retry.timeoutRetry(client, promise, request);
            }
        }
    }
}
